/*
 * pi3's Linux kernel Runtime Guard
 *
 * Component:
 *  - Exploit detection main module
 *
 * Notes:
 *  - None
 *
 * Timeline:
 *  - Created: 06.IX.2017
 *
 * Author:
 *  - Adam 'pi3' Zabrocki (http://pi3.com.pl)
 *
 */

#include "../../p_lkrg_main.h"

struct p_ed_global_variables p_ed_guard_globals;
unsigned long p_global_off_cookie;
unsigned long p_global_cnt_cookie;
struct kmem_cache *p_ed_wq_valid_cache = NULL;
struct kmem_cache *p_ed_pcfi_cache = NULL;

unsigned long p_pcfi_CPU_flags;

#define p_ed_pcfi_alloc()      kmem_cache_alloc(p_ed_pcfi_cache, GFP_ATOMIC)
#define p_ed_pcfi_free(name)   kmem_cache_free(p_ed_pcfi_cache, (void *)(name))

static int p_cmp_tasks(struct p_ed_process *p_orig, struct task_struct *p_current, char p_kill);
static unsigned int p_iterate_lkrg_tasks_paranoid(void);

static const struct p_functions_hooks {

   const char *name;
   int (*install)(int p_isra);
   void (*uninstall)(void);
   int p_fatal;
   const char *p_error_message;
   int is_isra_safe;

} p_functions_hooks_array[] = {
   {
     "security_bprm_committing_creds",
     p_install_security_bprm_committing_creds_hook,
     p_uninstall_security_bprm_committing_creds_hook,
     1,
     NULL,
     1
   },
   {
     "security_bprm_committed_creds",
     p_install_security_bprm_committed_creds_hook,
     p_uninstall_security_bprm_committed_creds_hook,
     1,
     NULL,
     1
   },
   { "call_usermodehelper",
     p_install_call_usermodehelper_hook,
     p_uninstall_call_usermodehelper_hook,
     0,
     "LKRG won't be able to guard UMH interface :( Other functionalities are going to be enforced.",
     0
   },
   { "call_usermodehelper_exec",
     p_install_call_usermodehelper_exec_hook,
     p_uninstall_call_usermodehelper_exec_hook,
     0,
     "LKRG won't enforce validation on 'call_usermodehelper_exec'",
     1
   },
   { "wake_up_new_task",
     p_install_wake_up_new_task_hook,
     p_uninstall_wake_up_new_task_hook,
     1,
     NULL,
     0
   },
   { "do_exit",
     p_install_do_exit_hook,
     p_uninstall_do_exit_hook,
     1,
     NULL,
     1
   },
   { "security_ptrace_access",
     p_install_security_ptrace_access_hook,
     p_uninstall_security_ptrace_access_hook,
     0,
     "LKRG won't enforce validation on 'security_ptrace_access'",
     0
   },
   { "sys_setuid",
     p_install_sys_setuid_hook,
     p_uninstall_sys_setuid_hook,
     1,
     NULL,
     0
   },
   { "sys_setreuid",
     p_install_sys_setreuid_hook,
     p_uninstall_sys_setreuid_hook,
     1,
     NULL,
     0
   },
   { "sys_setresuid",
     p_install_sys_setresuid_hook,
     p_uninstall_sys_setresuid_hook,
     1,
     NULL,
     0
   },
   { "sys_setfsuid",
     p_install_sys_setfsuid_hook,
     p_uninstall_sys_setfsuid_hook,
     1,
     NULL,
     0
   },
   { "sys_setgid",
     p_install_sys_setgid_hook,
     p_uninstall_sys_setgid_hook,
     1,
     NULL,
     0
   },
   { "sys_setregid",
     p_install_sys_setregid_hook,
     p_uninstall_sys_setregid_hook,
     1,
     NULL,
     0
   },
   { "sys_setresgid",
     p_install_sys_setresgid_hook,
     p_uninstall_sys_setresgid_hook,
     1,
     NULL,
     0
   },
   { "sys_setfsgid",
     p_install_sys_setfsgid_hook,
     p_uninstall_sys_setfsgid_hook,
     1,
     NULL,
     0
   },
   { "set_current_groups",
     p_install_set_current_groups_hook,
     p_uninstall_set_current_groups_hook,
     1,
     NULL,
     1
   },
   { "generic_permission",
     p_install_generic_permission_hook,
     p_uninstall_generic_permission_hook,
     0,
     "LKRG won't enforce validation on 'generic_permission'",
     1
   },
#ifdef CONFIG_SECURITY_SELINUX
   { "sel_write_enforce",
     p_install_sel_write_enforce_hook,
     p_uninstall_sel_write_enforce_hook,
     1,
     NULL,
     1
   },
#endif
   { "seccomp",
     p_install_seccomp_hook,
     p_uninstall_seccomp_hook,
     1,
     NULL,
     1
   },
   { "sys_unshare",
     p_install_sys_unshare_hook,
     p_uninstall_sys_unshare_hook,
     1,
     NULL,
     1
   },
   /* Caps. */
   { "sys_capset",
     p_install_sys_capset_hook,
     p_uninstall_sys_capset_hook,
     1,
     NULL,
     0
   },
   { "cap_task_prctl",
     p_install_cap_task_prctl_hook,
     p_uninstall_cap_task_prctl_hook,
     1,
     NULL,
     1
   },
   /* Keyring */
   { "key_change_session_keyring",
     p_install_key_change_session_keyring_hook,
     p_uninstall_key_change_session_keyring_hook,
     1,
     NULL,
     1
   },
   { "sys_add_key",
     p_install_sys_add_key_hook,
     p_uninstall_sys_add_key_hook,
     1,
     NULL,
     0
   },
   { "sys_request_key",
     p_install_sys_request_key_hook,
     p_uninstall_sys_request_key_hook,
     1,
     NULL,
     0
   },
   { "sys_keyctl",
     p_install_sys_keyctl_hook,
     p_uninstall_sys_keyctl_hook,
     1,
     NULL,
     0
   },
#ifdef CONFIG_COMPAT
   { "compat_sys_keyctl",
     p_install_compat_sys_keyctl_hook,
     p_uninstall_compat_sys_keyctl_hook,
     1,
     NULL,
     0
   },
 #ifdef P_SYSCALL_LAYOUT_4_17
  #ifdef CONFIG_X86
   { "compat_sys_capset",
     p_install_compat_sys_capset_hook,
     p_uninstall_compat_sys_capset_hook,
     1,
     NULL,
     0
   },
   { "compat_sys_add_key",
     p_install_compat_sys_add_key_hook,
     p_uninstall_compat_sys_add_key_hook,
     1,
     NULL,
     0
   },
   { "compat_sys_request_key",
     p_install_compat_sys_request_key_hook,
     p_uninstall_compat_sys_request_key_hook,
     1,
     NULL,
     0
   },
  #endif /* CONFIG_X86 */
 #endif /* P_SYSCALL_LAYOUT_4_17 */
#endif /* CONFIG_COMPAT */
#ifdef CONFIG_X86_X32
 #ifdef P_SYSCALL_LAYOUT_4_17
   { "x32_sys_keyctl",
     p_install_x32_sys_keyctl_hook,
     p_uninstall_x32_sys_keyctl_hook,
     1,
     NULL,
     0
   },
 #endif /* P_SYSCALL_LAYOUT_4_17 */
#endif /* CONFIG_X86_X32 */
   { "override_creds",
     p_install_override_creds_hook,
     p_uninstall_override_creds_hook,
     1,
     NULL,
     0
   },
   { "revert_creds",
     p_install_revert_creds_hook,
     p_uninstall_revert_creds_hook,
     1,
     NULL,
     0
   },
   /* Namespaces. */
   { "sys_setns",
     p_install_sys_setns_hook,
     p_uninstall_sys_setns_hook,
     1,
     NULL,
     0
   },
   /* OverlayFS
    *
    * OverlayFS might not be installed in that system - it is not critical
    * scenario. If OverlayFS is installed, used but not found (unlikely)
    * in worst case, we might have FP. Continue...
    */
   { "ovl_create_or_link",
     p_install_ovl_create_or_link_hook,
     p_uninstall_ovl_create_or_link_hook,
     0,
     "Can't hook 'ovl_create_or_link' function. This is expected if you are not using OverlayFS.",
     1
   },
   /* pCFI */
   { "pcfi_mark_inode_dirty",
     p_install_pcfi_mark_inode_dirty_hook,
     p_uninstall_pcfi_mark_inode_dirty_hook,
     0,
     "LKRG won't enforce pCFI validation on 'mark_inode_dirty'",
     0
   },
   { "pcfi_schedule",
     p_install_pcfi_schedule_hook,
     p_uninstall_pcfi_schedule_hook,
     0,
     "LKRG won't enforce pCFI validation on 'schedule'",
     1
   },
   { "pcfi___queue_work",
     p_install_pcfi___queue_work_hook,
     p_uninstall_pcfi___queue_work_hook,
     0,
     "LKRG won't enforce validation on '__queue_work'",
     1
   },
   { "pcfi_lookup_fast",
     p_install_pcfi_lookup_fast_hook,
     p_uninstall_pcfi_lookup_fast_hook,
     0,
     "LKRG won't enforce pCFI validation on 'lookup_fast'",
     1
   },
   { "capable",
     p_install_capable_hook,
     p_uninstall_capable_hook,
     0,
     "LKRG won't enforce validation on 'capable'",
     1
   },
   { "scm_send",
     p_install_scm_send_hook,
     p_uninstall_scm_send_hook,
     0,
     "LKRG won't enforce validation on 'scm_send'",
     1
   },
   { NULL, NULL, NULL, 1, NULL, 0 }
};

static void p_ed_wq_valid_cache_zero(void *p_arg) {

   struct work_struct *p_struct = p_arg;

   memset(p_struct, 0, sizeof(struct work_struct));
}

int p_ed_wq_valid_cache_init(void) {

   if ( (p_ed_wq_valid_cache = kmem_cache_create("p_ed_wq_valid_cache", sizeof(struct work_struct),
                                                 0, SLAB_HWCACHE_ALIGN, p_ed_wq_valid_cache_zero)) == NULL) {
      p_print_log(P_LKRG_ERR, "kmem_cache_create() for exploit detection validation error! :(\n");
      return -ENOMEM;
   }

   return P_LKRG_SUCCESS;
}

static void p_ed_wq_valid_cache_delete(void) {

   flush_workqueue(system_unbound_wq);
   if (p_ed_wq_valid_cache) {
      kmem_cache_destroy(p_ed_wq_valid_cache);
      p_ed_wq_valid_cache = NULL;
   }
}

notrace void p_dump_creds(struct p_cred *p_where, const struct cred *p_from) {

   /* Get reference to cred */
   get_cred(p_from);

   /* Track process's capabilities */
   memcpy(&p_where->cap_inheritable, &p_from->cap_inheritable, sizeof(kernel_cap_t));
   memcpy(&p_where->cap_permitted, &p_from->cap_permitted, sizeof(kernel_cap_t));
   memcpy(&p_where->cap_effective, &p_from->cap_effective, sizeof(kernel_cap_t));
   memcpy(&p_where->cap_bset, &p_from->cap_bset, sizeof(kernel_cap_t));
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
   memcpy(&p_where->cap_ambient, &p_from->cap_ambient, sizeof(kernel_cap_t));
#endif

   /* Track process's IDs */
   p_set_uid(&p_where->uid, p_get_uid(&p_from->uid));
   p_set_gid(&p_where->gid, p_get_gid(&p_from->gid));
   p_set_uid(&p_where->suid, p_get_uid(&p_from->suid));
   p_set_gid(&p_where->sgid, p_get_gid(&p_from->sgid));
   p_set_uid(&p_where->euid, p_get_uid(&p_from->euid));
   p_set_gid(&p_where->egid, p_get_gid(&p_from->egid));
   p_set_uid(&p_where->fsuid, p_get_uid(&p_from->fsuid));
   p_set_gid(&p_where->fsgid, p_get_gid(&p_from->fsgid));

   /* Track process's securebits - TODO: research */
   p_where->securebits = p_from->securebits;

   /* Track process's critical pointers */
   p_where->user     = p_from->user;
   p_where->user_ns  = p_from->user_ns;

   /* Release reference to cred */
   put_cred(p_from);
}

notrace void p_dump_seccomp(struct p_seccomp *p_sec, struct task_struct *p_task) {

   P_SYM(p_get_seccomp_filter)(p_task);
   p_sec->sec.mode    = p_task->seccomp.mode;   // Mode
   p_sec->sec.filter  = p_task->seccomp.filter; // Filter
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0)
   if (test_task_syscall_work(p_task,SECCOMP))
#else
   if (test_tsk_thread_flag(p_task,TIF_SECCOMP))
#endif
      p_sec->flag = 1;
   else
      p_sec->flag = 0;
   p_sec->flag_sync_thread = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
   P_SYM(p_put_seccomp_filter)(p_task->seccomp.filter);
#else
   P_SYM(p_put_seccomp_filter)(p_task);
#endif

}

#if defined(P_VERIFY_ADDR_LIMIT)
notrace static inline unsigned long p_get_addr_limit(struct task_struct *p_task) {

/* X86(-64)*/
#if defined(CONFIG_X86)

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0) && LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0)

   return p_task->thread.addr_limit.seg;

#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)

   struct thread_info *p_ti = task_thread_info(p_task);

   return p_ti->addr_limit.seg;

#endif

/* ARM(64) */
#elif defined(CONFIG_ARM) || defined(CONFIG_ARM64)

   struct thread_info *p_ti = task_thread_info(p_task);

   return p_ti->addr_limit;

#endif
}
#endif

notrace void p_verify_addr_limit(struct p_ed_process *p_orig, struct task_struct *p_current) {

#if defined(P_VERIFY_ADDR_LIMIT)

   unsigned long p_addr_limit =
#if defined(CONFIG_X86)
                                 p_orig->p_ed_task.p_addr_limit.seg;
#elif defined(CONFIG_ARM) || defined(CONFIG_ARM64)
                                 p_orig->p_ed_task.p_addr_limit;
#endif

   /* Verify addr_limit */
   if (p_addr_limit != p_get_addr_limit(p_current)) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Detected ADDR_LIMIT segment corruption! "
                                  "process[%d | %s] has different segment address! [%lx vs %lx]\n",
                                  task_pid_nr(p_current), p_current->comm,
                                  p_addr_limit,
                                  p_get_addr_limit(p_current));
      dump_stack();
      // kill this process!
      p_ed_kill_task_by_task(p_current);
   }

#endif
}

notrace static inline void p_dump_addr_limit(mm_segment_t *p_addr_limit, struct task_struct *p_task) {

#if defined(P_VERIFY_ADDR_LIMIT)

#if defined(CONFIG_X86)
   p_addr_limit->seg =
#elif defined(CONFIG_ARM) || defined(CONFIG_ARM64)
   *p_addr_limit =
#endif
                       p_get_addr_limit(p_task);
#endif
}

notrace void p_update_ed_process(struct p_ed_process *p_source, struct task_struct *p_task, char p_stack) {

   rcu_read_lock();
   get_task_struct(p_task);
   /* Track process's metadata */
   p_source->p_ed_task.p_pid                      = p_task->pid;
   p_source->p_ed_task.p_cred_ptr                 = rcu_dereference(p_task->cred);
   p_source->p_ed_task.p_real_cred_ptr            = rcu_dereference(p_task->real_cred);
   if (p_stack)
      p_source->p_ed_task.p_stack                 = p_task->stack;
   /* Namespaces */
   p_source->p_ed_task.p_nsproxy                  = p_task->nsproxy;
   p_source->p_ed_task.p_ns.uts_ns                = p_task->nsproxy->uts_ns;
   p_source->p_ed_task.p_ns.ipc_ns                = p_task->nsproxy->ipc_ns;
   p_source->p_ed_task.p_ns.mnt_ns                = p_task->nsproxy->mnt_ns;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
   p_source->p_ed_task.p_ns.pid_ns_for_children   = p_task->nsproxy->pid_ns_for_children;
#else
   p_source->p_ed_task.p_ns.pid_ns                = p_task->nsproxy->pid_ns;
#endif
   p_source->p_ed_task.p_ns.net_ns                = p_task->nsproxy->net_ns;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
   p_source->p_ed_task.p_ns.cgroup_ns             = p_task->nsproxy->cgroup_ns;
#endif
   /* Creds */
   p_dump_creds(&p_source->p_ed_task.p_cred, p_source->p_ed_task.p_cred_ptr);
   p_dump_creds(&p_source->p_ed_task.p_real_cred, p_source->p_ed_task.p_real_cred_ptr);
   /* Seccomp */
   p_dump_seccomp(&p_source->p_ed_task.p_sec, p_task);
   /* addr_limit */
#if defined(P_VERIFY_ADDR_LIMIT)
   p_dump_addr_limit(&p_source->p_ed_task.p_addr_limit, p_task);
#endif
   /* Name */
   strncpy(p_source->p_ed_task.p_comm, p_task->comm, TASK_COMM_LEN);
   p_source->p_ed_task.p_comm[TASK_COMM_LEN] = 0;
   /* Should be last here to propagate potential glitching */
   wmb();
   p_source->p_ed_task.p_task            = p_task;
   put_task_struct(p_task);
   rcu_read_unlock();

}

#ifdef P_LKRG_TASK_OFF_DEBUG
struct p_lkrg_debug_off_flag_callers {

   char p_id;
   char *p_name;

} p_debug_off_flag_callers[] = {

   { 0, "RESERVED" },
   { 1, "RESERVED" },
   { 2, "RESERVED" },
   { 3, "RESERVED" },
   { 4, "p_x32_sys_keyctl_entry" },
   { 5, "p_x32_sys_keyctl_ret" },
   { 6, "p_cap_task_prctl_entry" },
   { 7, "p_cap_task_prctl_ret" },
   { 8, "p_sys_capset_entry" },
   { 9, "p_sys_capset_ret" },
   { 10, "p_compat_sys_add_key_entry" },
   { 11, "p_compat_sys_add_key_ret" },
   { 12, "p_compat_sys_capset_entry" },
   { 13, "p_compat_sys_capset_ret" },
   { 14, "RESERVED" },
   { 15, "RESERVED" },
   { 16, "RESERVED" },
   { 17, "RESERVED" },
   { 18, "p_compat_sys_keyctl_entry" },
   { 19, "p_compat_sys_keyctl_ret" },
   { 20, "p_compat_sys_request_key_entry" },
   { 21, "p_compat_sys_request_key_ret" },
   { 22, "p_key_change_session_keyring_entry" },
   { 23, "p_key_change_session_keyring_ret" },
   { 24, "p_sys_add_key_entry" },
   { 25, "p_sys_add_key_ret" },
   { 26, "p_sys_keyctl_entry" },
   { 27, "p_sys_keyctl_ret" },
   { 28, "p_sys_request_key_entry" },
   { 29, "p_sys_request_key_ret" },
   { 30, "p_ovl_create_or_link_ret" },
   { 31, "p_override_creds_entry" },
   { 32, "p_revert_creds_ret" },
   { 33, "p_seccomp_entry" },
   { 34, "p_seccomp_ret" },
   { 35, "p_set_current_groups_entry" },
   { 36, "p_set_current_groups_ret" },
   { 37, "p_security_bprm_committing_creds_entry" },
   { 38, "RESERVED" },
   { 39, "RESERVED" },
   { 40, "p_security_bprm_committed_creds_ret" },
   { 41, "p_sys_setfsgid_entry" },
   { 42, "p_sys_setfsgid_ret" },
   { 43, "p_sys_setfsuid_entry" },
   { 44, "p_sys_setfsuid_ret" },
   { 45, "p_sys_setgid_entry" },
   { 46, "p_sys_setgid_ret" },
   { 47, "p_sys_setns_entry" },
   { 48, "p_sys_setns_ret" },
   { 49, "p_sys_setregid_entry" },
   { 50, "p_sys_setregid_ret" },
   { 51, "p_sys_setresgid_entry" },
   { 52, "p_sys_setresgid_ret" },
   { 53, "p_sys_setresuid_entry" },
   { 54, "p_sys_setresuid_ret" },
   { 55, "p_sys_setreuid_entry" },
   { 56, "p_sys_setreuid_ret" },
   { 57, "p_sys_setuid_entry" },
   { 58, "p_sys_setuid_ret" },
   { 59, "p_sys_unshare_entry" },
   { 60, "p_sys_unshare_ret" },
   { 61, "p_wake_up_new_task_entry" },
   { 0, NULL }

};

struct p_lkrg_debug_off_flag_action {

   char p_id;
   char *p_name;

} p_debug_off_flag_action[] = {

   { 0, "OFF" },
   { 1, "ON" },
   { 2, "RESET" },
   { 3, "OVERRIDE OFF" },
   { 4, "OVERRIDE ON" },

};

notrace void p_debug_off_flag_off(struct p_ed_process *p_source, unsigned int p_id) {

   p_source->p_ed_task.p_off_debug_cnt++;

   if (p_source->p_ed_task.p_off_counter < P_LKRG_TASK_OFF_MAXBUF-1) {
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 0;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   } else {
      /* Mark that we are starting overriding ring-buffer */
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val = -1;
      /* Reset */
      p_source->p_ed_task.p_off_counter = 0;
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 0;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   }
}

notrace void p_debug_off_flag_on(struct p_ed_process *p_source, unsigned int p_id) {

   p_source->p_ed_task.p_off_debug_cnt--;

   if (p_source->p_ed_task.p_off_counter < P_LKRG_TASK_OFF_MAXBUF-1) {
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 1;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   } else {
      /* Mark that we are starting overriding ring-buffer */
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Reset */
      p_source->p_ed_task.p_off_counter = 0;
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 1;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   }
}

notrace void p_debug_off_flag_override_off(struct p_ed_process *p_source, unsigned int p_id, struct pt_regs *p_regs) {

   p_source->p_ed_task.p_off_debug_cnt++;

   if (p_source->p_ed_task.p_off_counter < P_LKRG_TASK_OFF_MAXBUF-1) {
      /* Report current event */
      /* Stack trace*/
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries =
             (unsigned long *)p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_internal_buf;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries =
          P_PCFI_STACK_BUF/sizeof(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries[0]);
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries = 0;
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries =
          stack_trace_save(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries,
                           p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries,
                           1);
#else
      save_stack_trace(&p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace);
#endif
      /* End */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 3;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   } else {
      /* Mark that we are starting overriding ring-buffer */
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val = -1;
      /* Reset */
      p_source->p_ed_task.p_off_counter = 0;
      /* Report current event */
      /* Stack trace*/
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries =
             (unsigned long *)p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_internal_buf;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries =
          P_PCFI_STACK_BUF/sizeof(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries[0]);
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries = 0;
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries =
          stack_trace_save(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries,
                           p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries,
                           1);
#else
      save_stack_trace(&p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace);
#endif
      /* End */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 3;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   }
}

notrace void p_debug_off_flag_override_on(struct p_ed_process *p_source, unsigned int p_id, struct pt_regs *p_regs) {

   p_source->p_ed_task.p_off_debug_cnt--;

   if (p_source->p_ed_task.p_off_counter < P_LKRG_TASK_OFF_MAXBUF-1) {
      /* Report current event */
      /* Stack trace*/
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries =
             (unsigned long *)p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_internal_buf;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries =
          P_PCFI_STACK_BUF/sizeof(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries[0]);
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries = 0;
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries =
          stack_trace_save(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries,
                           p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries,
                           1);
#else
      save_stack_trace(&p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace);
#endif
      /* End */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 4;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   } else {
      /* Mark that we are starting overriding ring-buffer */
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val = -1;
      /* Reset */
      p_source->p_ed_task.p_off_counter = 0;
      /* Report current event */
      /* Stack trace*/
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries =
             (unsigned long *)p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_internal_buf;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries =
          P_PCFI_STACK_BUF/sizeof(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries[0]);
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries = 0;
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.nr_entries =
          stack_trace_save(p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.entries,
                           p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace.max_entries,
                           1);
#else
      save_stack_trace(&p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_trace);
#endif
      /* End */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 4;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   }
}

notrace void p_debug_off_flag_reset(struct p_ed_process *p_source, unsigned int p_id) {

   p_source->p_ed_task.p_off_debug_cnt = 0;

   if (p_source->p_ed_task.p_off_counter < P_LKRG_TASK_OFF_MAXBUF-1) {
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 2;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   } else {
      /* Mark that we are starting overriding ring-buffer */
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off = -1;
      p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val = -1;
      /* Reset */
      p_source->p_ed_task.p_off_counter = 0;
      /* Report current event */
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_caller = p_id;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_action = 2;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_old_off =
                                                p_source->p_ed_task.p_off ^ p_global_off_cookie;
      p_source->p_ed_task.p_off_debug[p_source->p_ed_task.p_off_counter].p_debug_val =
                                                            p_source->p_ed_task.p_off_debug_cnt;
      /* Increment ring-buffer pointer */
      p_source->p_ed_task.p_off_counter++;
   }
}

notrace void p_debug_off_flag_dump_ring_buffer(struct p_ed_process *p_source) {

   unsigned int p_tmp;

   p_print_log(P_LKRG_CRIT, "OFF debug: normalization[0x%lx] cookie[0x%lx]\n",
               p_global_cnt_cookie,
               p_global_off_cookie);
   p_print_log(P_LKRG_CRIT, "Process[%d | %s] Parent[%d | %s] has [%d] entries:\n",
               p_source->p_ed_task.p_pid,
               p_source->p_ed_task.p_comm,
               p_source->p_ed_task.p_task->real_parent->pid,
               p_source->p_ed_task.p_task->real_parent->comm,
               p_source->p_ed_task.p_off_counter);

   if (p_source->p_ed_task.p_off_counter < 3 &&
       p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_caller == -1 &&
       p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_action == -1 &&
       p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_old_off == -1 &&
       p_source->p_ed_task.p_off_debug[P_LKRG_TASK_OFF_MAXBUF-1].p_debug_val == -1) {
      /* OK, ring buffer was overwritten. Dump a few entries before overwrite: */
      p_print_log(P_LKRG_CRIT, "Before overwrite:\n");
      for (p_tmp = P_LKRG_TASK_OFF_MAXBUF-1-6; p_tmp < P_LKRG_TASK_OFF_MAXBUF-1; p_tmp++) {
         p_print_log(P_LKRG_CRIT," => caller[%s] action[%s] old_off[0x%lx] debug_val[%d]\n",
                     p_debug_off_flag_callers[p_source->p_ed_task.p_off_debug[p_tmp].p_caller].p_name,
                     p_debug_off_flag_action[p_source->p_ed_task.p_off_debug[p_tmp].p_action].p_name,
                     p_source->p_ed_task.p_off_debug[p_tmp].p_old_off,
                     p_source->p_ed_task.p_off_debug[p_tmp].p_debug_val);
         if (p_source->p_ed_task.p_off_debug[p_tmp].p_action == 3 ||
             p_source->p_ed_task.p_off_debug[p_tmp].p_action == 4) {
            p_print_log(P_LKRG_CRIT,"Stack trace:\n");
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
            stack_trace_print(p_source->p_ed_task.p_off_debug[p_tmp].p_trace.entries,
                              p_source->p_ed_task.p_off_debug[p_tmp].p_trace.nr_entries,
                              0);
#else
            print_stack_trace(&p_source->p_ed_task.p_off_debug[p_tmp].p_trace, 0);
#endif
         }
      }
      p_print_log(P_LKRG_CRIT, "=== RING BUFFER OVERRIDE ===\n");
   }

   for (p_tmp = 0; p_tmp < p_source->p_ed_task.p_off_counter; p_tmp++) {
      p_print_log(P_LKRG_CRIT," => caller[%s] action[%s] old_off[0x%lx] debug_val[%d]\n",
                  p_debug_off_flag_callers[p_source->p_ed_task.p_off_debug[p_tmp].p_caller].p_name,
                  p_debug_off_flag_action[p_source->p_ed_task.p_off_debug[p_tmp].p_action].p_name,
                  p_source->p_ed_task.p_off_debug[p_tmp].p_old_off,
                  p_source->p_ed_task.p_off_debug[p_tmp].p_debug_val);
      if (p_source->p_ed_task.p_off_debug[p_tmp].p_action == 3 ||
          p_source->p_ed_task.p_off_debug[p_tmp].p_action == 4) {
         p_print_log(P_LKRG_CRIT,"Stack trace:\n");
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
         stack_trace_print(p_source->p_ed_task.p_off_debug[p_tmp].p_trace.entries,
                           p_source->p_ed_task.p_off_debug[p_tmp].p_trace.nr_entries,
                           0);
#else
         print_stack_trace(&p_source->p_ed_task.p_off_debug[p_tmp].p_trace, 0);
#endif
      }
   }
}
#endif

inline void p_validate_off_flag(struct p_ed_process *p_source, long p_val, int *p_ret) {

   if (likely(p_val == p_global_cnt_cookie))
      return;

   while (p_val > p_global_cnt_cookie) {
      p_val -= p_global_cnt_cookie;
      if (unlikely(p_val > (p_global_cnt_cookie << 3)))
         break;
   }

   if (unlikely(p_val != p_global_cnt_cookie)) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process[%d | %s] has corrupted 'off' flag!\n",
             p_source->p_ed_task.p_pid, p_source->p_ed_task.p_comm);
#ifdef P_LKRG_TASK_OFF_DEBUG
      p_print_log(P_LKRG_CRIT,
#else
      p_print_log(P_LKRG_INFO,
#endif
             "'off' flag[0x%lx] (normalization via 0x%lx)\n",
             p_val, p_global_cnt_cookie);
#ifdef P_LKRG_TASK_OFF_DEBUG
      p_debug_off_flag_dump_ring_buffer(p_source);
      dump_stack();
#endif
      // kill this process!
      rcu_read_lock();
      if (p_ret)
         (*p_ret)++;
      else
         p_ed_kill_task_by_task(p_source->p_ed_task.p_task);
      rcu_read_unlock();
   }
}

inline void p_ed_is_off_off(struct p_ed_process *p_source, long p_val) {

   if (p_val != p_global_cnt_cookie) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> ON process[%d | %s] has corrupted 'off' flag!\n",
             p_source->p_ed_task.p_pid, p_source->p_ed_task.p_comm);
#ifdef P_LKRG_TASK_OFF_DEBUG
      p_print_log(P_LKRG_CRIT,
#else
      p_print_log(P_LKRG_INFO,
#endif
             "'off' flag[0x%lx] (normalization via 0x%lx)\n",
             p_val, p_global_cnt_cookie);
#ifdef P_LKRG_TASK_OFF_DEBUG
      p_debug_off_flag_dump_ring_buffer(p_source);
      dump_stack();
#endif
      // kill this process!
      rcu_read_lock();
      p_ed_kill_task_by_task(p_source->p_ed_task.p_task);
      rcu_read_unlock();
   }
}

notrace int p_verify_ovl_create_or_link(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode

   p_validate_off_flag(p_source,p_off,NULL);   // Validate

   return p_off == 2 * p_global_cnt_cookie;
}

notrace void p_ed_is_off_off_wrap(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode
   p_ed_is_off_off(p_source,p_off);
}

notrace void p_ed_validate_off_flag_wrap(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode
   p_validate_off_flag(p_source,p_off,NULL);   // Validate
}

notrace void p_set_ed_process_on(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode

   p_off -= p_global_cnt_cookie;               // Normalize
   p_ed_is_off_off(p_source,p_off);            // Validate

   p_source->p_ed_task.p_off = p_off ^ p_global_off_cookie; // Encode
   p_source->p_ed_task.p_off_count = 0;
}

notrace void p_set_ed_process_off(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode

   p_ed_is_off_off(p_source,p_off);            // Validate
   p_off += p_global_cnt_cookie;               // Normalize

   p_source->p_ed_task.p_off = p_off ^ p_global_off_cookie;
}

notrace void p_set_ed_process_override_on(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode

   p_validate_off_flag(p_source,p_off,NULL);   // Validate
   p_off -= p_global_cnt_cookie;               // Normalize

   p_source->p_ed_task.p_off = p_off ^ p_global_off_cookie; // Encode
   if (p_off == p_global_cnt_cookie)
      p_source->p_ed_task.p_off_count = 0;
}

notrace void p_set_ed_process_override_off(struct p_ed_process *p_source) {

   register unsigned long p_off = p_source->p_ed_task.p_off ^ p_global_off_cookie; // Decode

   p_validate_off_flag(p_source,p_off,NULL);   // Validate
   p_off += p_global_cnt_cookie;               // Normalize

   p_source->p_ed_task.p_off = p_off ^ p_global_off_cookie;
}

notrace void p_reset_ed_flags(struct p_ed_process *p_source) {

   p_source->p_ed_task.p_off = p_global_cnt_cookie ^ p_global_off_cookie;
   p_source->p_ed_task.p_off_count = 0;

}

int p_print_task_f(void *p_arg) {

   struct task_struct *p_task = (struct task_struct *)p_arg;

   p_print_log(P_LKRG_CRIT,
                  "%s [%d]\n", p_task->comm, task_pid_nr(p_task));

   return P_LKRG_SUCCESS;
}

int p_dump_task_f(void *p_arg) {

   struct task_struct *p_task = (struct task_struct *)p_arg;
   struct p_ed_process *p_tmp;
   struct rb_root *p_root;

   if ( (p_tmp = p_alloc_ed_pids()) == NULL) {
      p_print_log(P_LKRG_ERR,
             "p_alloc_ed_pids() returned NULL for pid %d :(\n",p_task->pid);
      return P_LKRG_GENERAL_ERROR;
   }

   p_update_ed_process(p_tmp, p_task, 1);
//   p_set_ed_process_on(p_tmp);
   p_tmp->p_ed_task.p_off = p_global_cnt_cookie ^ p_global_off_cookie;
   p_tmp->p_ed_task.p_off_count = 0;
#ifdef P_LKRG_TASK_OFF_DEBUG
   p_tmp->p_ed_task.p_off_debug_cnt = p_tmp->p_ed_task.p_off_counter = 0;
#endif

   p_rb_init_ed_pid_node(&p_tmp->p_rb);
   p_root = p_rb_hash_tree_lookup(p_tmp->p_ed_task.p_pid);
   if (p_rb_add_ed_pid(p_root, p_tmp->p_ed_task.p_pid, p_tmp)) {
      p_print_log(P_LKRG_INFO,
                   "pid => %d is already inserted!\n",p_tmp->p_ed_task.p_pid);
      p_free_ed_pids(p_tmp);
      return 1;
   } else {
      p_print_log(P_LKRG_INFO,
                   "Inserting pid => %d\n", p_tmp->p_ed_task.p_pid);
   }

   return P_LKRG_SUCCESS;
}

int p_remove_task_pid_f(pid_t p_arg) {

   struct p_ed_process *p_tmp;
   struct rb_root *p_root;

   p_root = p_rb_hash_tree_lookup(p_arg);
   if ( (p_tmp = p_rb_find_ed_pid(p_root, p_arg)) == NULL) {
      // This process is not on the list!
      return P_LKRG_GENERAL_ERROR;
   }

   p_rb_del_ed_pid(p_root, p_tmp);
   p_print_log(P_LKRG_INFO, "Removing ED pid => %d\n", p_arg);

   return P_LKRG_SUCCESS;
}

static unsigned int p_iterate_processes(int (*p_func)(void *), char p_ver) {

   int p_ret;
   unsigned int p_err = 0;
   struct task_struct *p_ptmp, *p_tmp;
   unsigned long p_flags;

   p_tasks_read_lock(&p_flags);
   rcu_read_lock();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
   for_each_process_thread(p_ptmp, p_tmp) {
#else
   // tasklist_lock
   do_each_thread(p_ptmp, p_tmp) {
#endif

      get_task_struct(p_tmp);
      /* do not touch kernel threads or the global init */
      if (!p_is_ed_task(p_tmp)) {
         put_task_struct(p_tmp);
         continue;
      }

      if ( (p_ret = p_func(p_tmp)) != 0) {
         p_err++;
         p_print_log(P_LKRG_INFO,
                      "<Exploit Detection> Error[%d] during process[%d |%s] iteration!\n",
                                                           p_ret, task_pid_nr(p_tmp), p_tmp->comm);
         if (likely(p_ver)) {
            if (spin_is_locked(&p_tmp->sighand->siglock)) {
               p_regs_set_ip(task_pt_regs(p_tmp), -1);
            } else {
               p_ed_kill_task_by_task(p_tmp);
            }
         }
      }
      put_task_struct(p_tmp);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
   }
#else
   // tasklist_unlock
   } while_each_thread(p_ptmp, p_tmp);
#endif
   rcu_read_unlock();
   p_tasks_read_unlock(&p_flags);

   return p_err;
}

static unsigned int p_iterate_lkrg_tasks_paranoid(void) {

   int p_ret = 0;
   unsigned int i;
   struct rb_node *p_node;
   struct p_ed_process *p_tmp;
   struct task_struct *p_task = NULL;
   unsigned long p_flags;

   for (i=0; i<RB_HASH_SIZE; i++) {
      p_tasks_read_lock_raw(&p_rb_hash[i].p_lock.lock);
      rcu_read_lock();
      for (p_node = rb_first(&p_rb_hash[i].p_tree.tree); p_node; p_node = rb_next(p_node)) {
         p_tmp = rb_entry(p_node, struct p_ed_process, p_rb);
         if ( (p_task = pid_task(find_vpid(p_tmp->p_ed_task.p_pid), PIDTYPE_PID)) != NULL) {
            get_task_struct(p_task);
            /* do not touch kernel threads or the global init */
            if (p_is_ed_task(p_task) && p_task->state != TASK_DEAD) {
               if (p_cmp_tasks(p_tmp, p_task, 0x0)) {
                  p_ret++;
                  if (spin_is_locked(&p_task->sighand->siglock)) {
                     p_regs_set_ip(task_pt_regs(p_task), -1);
                  } else {
                     p_ed_kill_task_by_task(p_task);
                  }
               }
            }
            put_task_struct(p_task);
         }
      }
      rcu_read_unlock();
      p_tasks_read_unlock_raw(&p_rb_hash[i].p_lock.lock);
   }

   /* Before leaving, verify current task */
   p_tasks_read_lock(&p_flags);
   if (p_is_ed_task(current)) {
      p_validate_task_f(current);
   }
   p_tasks_read_unlock(&p_flags);

   return p_ret;
}

int p_cmp_creds(struct p_cred *p_orig, const struct cred *p_current_cred, struct task_struct *p_current, char p_opt) {

   int p_ret = 0;

   /* *UID */
   if (!uid_eq(p_orig->uid, p_current_cred->uid)) {
      if (p_opt) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process[%d | %s] has different UID! %d vs %d\n",
             task_pid_nr(p_current), p_current->comm,
             p_get_uid(&p_orig->uid), p_get_uid(&p_current_cred->uid));
      }
      p_ret++;
   }

   if (!uid_eq(p_orig->euid, p_current_cred->euid)) {
      if (p_opt) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process[%d | %s] has different EUID! %d vs %d\n",
             task_pid_nr(p_current), p_current->comm,
             p_get_uid(&p_orig->euid), p_get_uid(&p_current_cred->euid));
      }
      p_ret++;
   }

   if (!uid_eq(p_orig->suid, p_current_cred->suid)) {
      if (p_opt) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process[%d | %s] has different SUID! %d vs %d\n",
             task_pid_nr(p_current), p_current->comm,
             p_get_uid(&p_orig->suid), p_get_uid(&p_current_cred->suid));
      }
      p_ret++;
   }

   if (!uid_eq(p_orig->fsuid, p_current_cred->fsuid)) {
      if (p_opt) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process[%d | %s] has different FSUID! %d vs %d\n",
             task_pid_nr(p_current), p_current->comm,
             p_get_uid(&p_orig->fsuid), p_get_uid(&p_current_cred->fsuid));
      }
      p_ret++;
   }

   /* *GID */
   if (!gid_eq(p_orig->gid, p_current_cred->gid)) {
      if (p_opt) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process[%d | %s] has different GID! %d vs %d\n",
             task_pid_nr(p_current), p_current->comm,
             p_get_gid(&p_orig->gid), p_get_gid(&p_current_cred->gid));
      }
      p_ret++;
   }

   if (!gid_eq(p_orig->egid, p_current_cred->egid)) {
      if (p_opt) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process[%d | %s] has different EGID! %d vs %d\n",
             task_pid_nr(p_current), p_current->comm,
             p_get_gid(&p_orig->egid), p_get_gid(&p_current_cred->egid));
      }
      p_ret++;
   }

   if (!gid_eq(p_orig->sgid, p_current_cred->sgid)) {
      if (p_opt) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process[%d | %s] has different SGID! %d vs %d\n",
             task_pid_nr(p_current), p_current->comm,
             p_get_gid(&p_orig->sgid), p_get_gid(&p_current_cred->sgid));
      }
      p_ret++;
   }

   if (!gid_eq(p_orig->fsgid, p_current_cred->fsgid)) {
      if (p_opt) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process[%d | %s] has different FSGID! %d vs %d\n",
             task_pid_nr(p_current), p_current->comm,
             p_get_gid(&p_orig->fsgid), p_get_gid(&p_current_cred->fsgid));
      }
      p_ret++;
   }

   /* Namespaces */
   if (p_orig->user_ns != p_current_cred->user_ns) {
      if (p_opt) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process[%d | %s] has different user_namespace!\n",
             task_pid_nr(p_current),
             p_current->comm);
         p_print_log(P_LKRG_INFO, "user_namespace: orig[0x%lx] vs current[0x%lx]\n",
             (unsigned long)p_orig->user_ns,
             (unsigned long)p_current_cred->user_ns);
      }
      p_ret++;
   }

   return p_ret;
}

static int p_cmp_tasks(struct p_ed_process *p_orig, struct task_struct *p_current, char p_kill) {

   int p_ret = 0;
   char *p_sec_strings[3] = { "SECCOMP_MODE_DISABLED", "SECCOMP_MODE_STRICT", "SECCOMP_MODE_FILTER" };
   register long p_off = p_orig->p_ed_task.p_off ^ p_global_off_cookie;
   const struct cred *p_current_cred = NULL;
   const struct cred *p_current_real_cred = NULL;

   if (p_off - p_global_cnt_cookie) {
      if (p_kill)
         p_validate_off_flag(p_orig,p_off,NULL);     // Validate
      else
         p_validate_off_flag(p_orig,p_off,&p_ret);   // Validate

      p_orig->p_ed_task.p_off_count++;

      if (p_orig->p_ed_task.p_off_count > P_ED_PROCESS_OFF_MAX) {
         p_print_log(P_LKRG_INFO,
                "PID:%d [%s] p_off[0x%lx] / p_global_cnt[0x%lx] -> %ld | p_off_count[%u]\n",
                p_orig->p_ed_task.p_pid,
                p_orig->p_ed_task.p_comm,
                p_off, p_global_cnt_cookie,
                p_off/p_global_cnt_cookie,
                p_orig->p_ed_task.p_off_count);
         /* That's weird and it might be a potentially compromised process. Enforce validation now! */
/*
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Detected data corruption attack! "
                                  "process[%d | %s] has been disabled form checking %d times!\n",
                                  task_pid_nr(p_current), p_current->comm,
                                  p_orig->p_ed_task.p_off_count);
*/
//         p_ret++;
      }
      return 0;
   }


   if (p_orig->p_ed_task.p_task != p_current) {
      /*
       * On heavily loaded SMP machines, in a very rare corner case situation,
       * it is possible to hit an annoying kretprobe glitch.
       * If you are one of the "lucky" guys who hit this problem for one of the processes
       * being monitored by the Exploit Detection (ED) feature, you are a good candidate
       * to hit another problem. If at some point kernel decided to reuse already unused
       * pid (from the process which was affected by the glitching scenario) you are an even
       * better candidate to hit a potential little race condition in a function return
       * from do_fork().
       * When p_do_fork_ret() is invoked it tries to get a spin_lock for internal ED database
       * (which is red-black tree identified by p_rb_ed_pids_lock lock). If another CPU/core is
       * executing ED validation routine p_ed_enforce_validation() it might be faster to get
       * this lock before p_do_fork_ret() does (race condition). In that case, p_do_fork_ret()
       * does a busy wait, and the other CPU does verification. If you are the most "lucky" guy
       * who hit the glitching problem and pid reuse scenario for the process which supposed to
       * do attributes update for ED database from the p_do_fork_ret() function you will generate
       * a one-time FP. As soon as FP happens and verification routine unlocks the ED database,
       * p_do_fork_ret() kicks-in and fixes attributes, and FP will never happen again.
       * This is a very rare corner-case situation which can only be possible if you meet all
       * the problems together affecting exactly the same process (kretprobe glitch + pid reuse
       * + race condition on a heavily loaded SMP machine). It is possible to mitigate this
       * problem and we do it here.
       */
      p_print_log(P_LKRG_WARN,
             "<Exploit Detection> Potential kretprobe glitch detected for process[%s] vs orig[%s]\n",
             p_current->comm,
             p_orig->p_ed_task.p_comm);
      p_print_log(P_LKRG_INFO,
             "process[0x%lx | %d | %s] vs orig[0x%lx | %d | %s]\n",
             (unsigned long)p_current,
             task_pid_nr(p_current),
             p_current->comm,
             (unsigned long)p_orig->p_ed_task.p_task,
             p_orig->p_ed_task.p_pid,
             p_orig->p_ed_task.p_comm);
      return 0;
   }

   /*
    * Fetch pointers first
    */
   p_current_cred = rcu_dereference(p_current->cred);
   /* Get reference to cred */
   get_cred(p_current_cred);
   p_current_real_cred = rcu_dereference(p_current->real_cred);
   /* Get reference to real_cred */
   get_cred(p_current_real_cred);

   /* Validate stack first */
   if (p_ed_pcfi_validate_sp(p_current,p_orig,p_get_thread_sp(p_current))) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Detected stack pointer corruption (ROP?) - pCFI violation: process[%s | %d] !!!\n",
             p_current->comm,task_pid_nr(p_current));
      // kill this process! Another signal will be sent again from the caller function - it's OK.
      if (p_kill)
         p_pcfi_kill_task_by_task(p_current);
      p_ret++;
   }

   if (p_orig->p_ed_task.p_cred_ptr != p_current_cred) {
      if (p_cmp_creds(&p_orig->p_ed_task.p_cred, p_current_cred, p_current, 0x0)) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Detected pointer swapping attack!"
                                  "process[%d | %s] has different 'cred' pointer\n",
                                  task_pid_nr(p_current),
                                  p_current->comm);
         p_print_log(P_LKRG_INFO, "Original[0x%lx] vs current[0x%lx]\n",
                                  (unsigned long)p_orig->p_ed_task.p_cred_ptr,
                                  (unsigned long)p_current->cred);
         p_ret++;
      }
   }

   if (p_orig->p_ed_task.p_real_cred_ptr != p_current_real_cred) {
      if (p_cmp_creds(&p_orig->p_ed_task.p_real_cred, p_current_real_cred, p_current, 0x0)) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Detected pointer swapping attack!"
                                  "process[%d | %s] has different 'real_cred' pointer\n",
                                  task_pid_nr(p_current),
                                  p_current->comm);
         p_print_log(P_LKRG_INFO, "Original[0x%lx] vs current[0x%lx]\n",
                                  (unsigned long)p_orig->p_ed_task.p_real_cred_ptr,
                                  (unsigned long)p_current->real_cred);
         p_ret++;
      }
   }

   p_ret += p_cmp_creds(&p_orig->p_ed_task.p_cred, p_current_cred, p_current, 0x1);
   if (p_ret)
      p_ret += p_cmp_creds(&p_orig->p_ed_task.p_real_cred, p_current_real_cred, p_current, 0x1);

   /* Namespaces */
   if (p_orig->p_ed_task.p_nsproxy != p_current->nsproxy) {
      p_print_log(P_LKRG_CRIT,
          "<Exploit Detection> Detected namespace escape attack!"
                               "process[%d | %s] has different 'nsproxy' pointer\n",
                               task_pid_nr(p_current),
                               p_current->comm);
      p_print_log(P_LKRG_INFO, "Original[0x%lx] vs current[0x%lx]\n",
                               (unsigned long)p_orig->p_ed_task.p_nsproxy,
                               (unsigned long)p_current->nsproxy);
      p_ret++;
   }

   if (p_orig->p_ed_task.p_ns.uts_ns != p_current->nsproxy->uts_ns) {
      p_print_log(P_LKRG_CRIT,
          "<Exploit Detection> Detected UTS namespace escape attack!"
                               "process[%d | %s] has different 'nsproxy->uts_ns' pointer\n",
                               task_pid_nr(p_current),
                               p_current->comm);
      p_print_log(P_LKRG_INFO, "Original[0x%lx] vs current[0x%lx]\n",
                               (unsigned long)p_orig->p_ed_task.p_ns.uts_ns,
                               (unsigned long)p_current->nsproxy->uts_ns);
      p_ret++;
   }

   if (p_orig->p_ed_task.p_ns.ipc_ns != p_current->nsproxy->ipc_ns) {
      p_print_log(P_LKRG_CRIT,
          "<Exploit Detection> Detected IPC namespace escape attack!"
                               "process[%d | %s] has different 'nsproxy->ipc_ns' pointer\n",
                               task_pid_nr(p_current),
                               p_current->comm);
      p_print_log(P_LKRG_INFO, "Original[0x%lx] vs current[0x%lx]\n",
                               (unsigned long)p_orig->p_ed_task.p_ns.ipc_ns,
                               (unsigned long)p_current->nsproxy->ipc_ns);
      p_ret++;
   }

   if (p_orig->p_ed_task.p_ns.mnt_ns != p_current->nsproxy->mnt_ns) {
      p_print_log(P_LKRG_CRIT,
          "<Exploit Detection> Detected MOUNT namespace escape attack!"
                               "process[%d | %s] has different 'nsproxy->mnt_ns' pointer\n",
                               task_pid_nr(p_current),
                               p_current->comm);
      p_print_log(P_LKRG_INFO, "Original[0x%lx] vs current[0x%lx]\n",
                               (unsigned long)p_orig->p_ed_task.p_ns.mnt_ns,
                               (unsigned long)p_current->nsproxy->mnt_ns);
      p_ret++;
   }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
   if (p_orig->p_ed_task.p_ns.pid_ns_for_children != p_current->nsproxy->pid_ns_for_children) {
#else
   if (p_orig->p_ed_task.p_ns.pid_ns != p_current->nsproxy->pid_ns) {
#endif
      p_print_log(P_LKRG_CRIT,
          "<Exploit Detection> Detected PID namespace escape attack!"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
                               "process[%d | %s] has different 'nsproxy->pid_ns_for_children' pointer\n",
#else
                               "process[%d | %s] has different 'nsproxy->pid_ns' pointer\n",
#endif
                               task_pid_nr(p_current),
                               p_current->comm);
      p_print_log(P_LKRG_INFO, "Original[0x%lx] vs current[0x%lx]\n",
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
                               (unsigned long)p_orig->p_ed_task.p_ns.pid_ns_for_children,
                               (unsigned long)p_current->nsproxy->pid_ns_for_children);
#else
                               (unsigned long)p_orig->p_ed_task.p_ns.pid_ns,
                               (unsigned long)p_current->nsproxy->pid_ns);
#endif
      p_ret++;
   }

   if (p_orig->p_ed_task.p_ns.net_ns != p_current->nsproxy->net_ns) {
      p_print_log(P_LKRG_CRIT,
          "<Exploit Detection> Detected NET namespace escape attack!"
                               "process[%d | %s] has different 'nsproxy->net_ns' pointer\n",
                               task_pid_nr(p_current),
                               p_current->comm);
      p_print_log(P_LKRG_INFO, "Original[0x%lx] vs current[0x%lx]\n",
                               (unsigned long)p_orig->p_ed_task.p_ns.net_ns,
                               (unsigned long)p_current->nsproxy->net_ns);
      p_ret++;
   }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
   if (p_orig->p_ed_task.p_ns.cgroup_ns != p_current->nsproxy->cgroup_ns) {
      p_print_log(P_LKRG_CRIT,
          "<Exploit Detection> Detected CGROUP namespace escape attack!"
                               "process[%d | %s] has different 'nsproxy->cgroup_ns' pointer\n",
                               task_pid_nr(p_current),
                               p_current->comm);
      p_print_log(P_LKRG_INFO, "Original[0x%lx] vs current[0x%lx]\n",
                               (unsigned long)p_orig->p_ed_task.p_ns.cgroup_ns,
                               (unsigned long)p_current->nsproxy->cgroup_ns);
      p_ret++;
   }
#endif

   /* Seccomp */
   if (p_orig->p_ed_task.p_sec.flag) { // SECCOMP was enabled so it make sense to compare...
      P_SYM(p_get_seccomp_filter)(p_current);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0)
      if (test_task_syscall_work(p_current,SECCOMP) != p_orig->p_ed_task.p_sec.flag) {
#else
      if (test_tsk_thread_flag(p_current,TIF_SECCOMP) != p_orig->p_ed_task.p_sec.flag) {
#endif
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Detected SECCOMP corruption!"
                                  "process[%d | %s] has corrupted TIF_SECCOMP flag! [%d vs %d]\n",
                                  task_pid_nr(p_current),
                                  p_current->comm,
                                  p_orig->p_ed_task.p_sec.flag,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0)
                                  test_task_syscall_work(p_current,SECCOMP)
#else
                                  test_tsk_thread_flag(p_current,TIF_SECCOMP)
#endif
                                  );
         p_ret++;
      }

      if (p_orig->p_ed_task.p_sec.sec.mode != p_current->seccomp.mode) {
         if (p_current->seccomp.mode < 0 || p_current->seccomp.mode > 2
             || p_orig->p_ed_task.p_sec.sec.mode < 0 || p_orig->p_ed_task.p_sec.sec.mode > 2) {
            p_print_log(P_LKRG_CRIT,
                "<Exploit Detection> Detected SECCOMP corruption!"
                                     "process[%d | %s] has UNKNOWN different SECCOMP mode! [%d vs %d]\n",
                                     task_pid_nr(p_current), p_current->comm,
                                     p_orig->p_ed_task.p_sec.sec.mode, p_current->seccomp.mode);
         } else {
            p_print_log(P_LKRG_CRIT,
                "<Exploit Detection> Detected SECCOMP corruption!"
                                     "process[%d | %s] has different SECCOMP mode! [%s vs %s]\n",
                                     task_pid_nr(p_current), p_current->comm,
                                     p_sec_strings[p_orig->p_ed_task.p_sec.sec.mode],
                                     p_sec_strings[p_current->seccomp.mode]);
         }
         p_ret++;
      }

      if (p_orig->p_ed_task.p_sec.sec.filter != p_current->seccomp.filter) {
         p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Detected SECCOMP corruption!"
                                  "process[%d | %s] has different SECCOMP filter pointer!\n",
                                  task_pid_nr(p_current),
                                  p_current->comm);
         p_print_log(P_LKRG_INFO, "Original[0x%lx] vs current[0x%lx]\n",
                                  (unsigned long)p_orig->p_ed_task.p_sec.sec.filter,
                                  (unsigned long)p_current->seccomp.filter);
         p_ret++;
      }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
      P_SYM(p_put_seccomp_filter)(p_current->seccomp.filter);
#else
      P_SYM(p_put_seccomp_filter)(p_current);
#endif
   }

   /* Release reference to cred */
   put_cred(p_current_cred);
   /* Release reference to real_cred */
   put_cred(p_current_real_cred);

   return p_ret;
}

int p_validate_task_f(void *p_arg) {

   int p_ret = P_LKRG_SUCCESS;
   struct p_ed_process *p_tmp;
   struct task_struct *p_task = (struct task_struct *)p_arg;

   rcu_read_lock();
   get_task_struct(p_task);

   if ( (p_tmp = p_find_ed_by_pid(task_pid_nr(p_task))) == NULL) {
      // This process is not on the list!
      if (p_task->state != TASK_DEAD) {
         p_ret = P_LKRG_GENERAL_ERROR;
         p_print_log(P_LKRG_INFO,
                  "<Exploit Detection> Can't find process[%d |%s] in internal tracking list!\n",
                                                                   task_pid_nr(p_task), p_task->comm);
      }
      goto p_validate_task_out;
   }

   if (p_cmp_tasks(p_tmp, p_task, 0x1)) {
      // kill this process!
      p_ed_kill_task_by_task(p_task);
   }

p_validate_task_out:

   put_task_struct(p_task);
   rcu_read_unlock();

   return p_ret;
}

#ifdef CONFIG_SECURITY_SELINUX
static void p_validate_selinux(void) {
   unsigned long p_flags;

   do {
      p_lkrg_counter_lock_lock(&p_ed_guard_globals.p_selinux_lock, &p_flags);
      if (p_lkrg_counter_lock_val_read(&p_ed_guard_globals.p_selinux_lock)) {
         p_lkrg_counter_lock_unlock(&p_ed_guard_globals.p_selinux_lock, &p_flags);
         schedule();
         continue;
      } else {
         break;
      }
   } while(1);

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
   if (p_ed_guard_globals.p_selinux.p_selinux_enabled != *P_SYM(p_selinux_enabled)) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Detected data corruption against SELINUX! 'selinux_enabled' has "
                                  "different value [%d vs %d] than expected. Original value will be restored!\n",
                                  *P_SYM(p_selinux_enabled),p_ed_guard_globals.p_selinux.p_selinux_enabled);
      *P_SYM(p_selinux_enabled) = p_ed_guard_globals.p_selinux.p_selinux_enabled;
   }
#endif

#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)
   if (p_ed_guard_globals.p_selinux.p_selinux_state.enforcing != P_SYM(p_selinux_state)->enforcing) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Detected data corruption against SELINUX! 'selinux_state->enforcing' has "
                                  "different value [%d vs %d] than expected!\n",
                                  P_SYM(p_selinux_state)->enforcing,p_ed_guard_globals.p_selinux.p_selinux_state.enforcing);

      switch (P_CTRL(p_kint_enforce)) {

         /* Panic */
         case 2:
           // OK, we need to crash the kernel now
           panic(P_LKRG_SIGNATURE "SELinux Integrity verification failed! Killing the kernel...\n");
           break;

         case 1:
           p_print_log(P_LKRG_CRIT, "Original value will be restored!\n");
           P_SYM(p_selinux_state)->enforcing = p_ed_guard_globals.p_selinux.p_selinux_state.enforcing;
           break;

         case 0:
           p_print_log(P_LKRG_CRIT, "Accepting new state!\n");
           p_ed_guard_globals.p_selinux.p_selinux_state.enforcing = P_SYM(p_selinux_state)->enforcing;
           break;

      }

   }
 #else
   if (p_ed_guard_globals.p_selinux.p_selinux_enforcing != *P_SYM(p_selinux_enforcing)) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Detected data corruption against SELINUX! 'selinux_enforcing' has "
                                  "different value [%d vs %d] than expected. Original value will be restored!\n",
                                  *P_SYM(p_selinux_enforcing),p_ed_guard_globals.p_selinux.p_selinux_enforcing);

      switch (P_CTRL(p_kint_enforce)) {

         /* Panic */
         case 2:
           // OK, we need to crash the kernel now
           panic(P_LKRG_SIGNATURE "SELinux Integrity verification failed! Killing the kernel...\n");
           break;

         case 1:
           p_print_log(P_LKRG_CRIT, "Original value will be restored!\n");
           *P_SYM(p_selinux_enforcing) = p_ed_guard_globals.p_selinux.p_selinux_enforcing;
           break;

         case 0:
           p_print_log(P_LKRG_CRIT, "Accepting new state!\n");
           p_ed_guard_globals.p_selinux.p_selinux_enforcing = *P_SYM(p_selinux_enforcing);
           break;

      }

   }
 #endif
#endif
   p_lkrg_counter_lock_unlock(&p_ed_guard_globals.p_selinux_lock, &p_flags);
}
#endif

static void p_ed_wq_valid_work(struct work_struct *p_work) {

#ifdef CONFIG_SECURITY_SELINUX
   // SELinux
   p_validate_selinux();
#endif

   /* Free the worker struct */
   if (p_work) {
      p_ed_free_valid(p_work);
   }
}

static void p_ed_validate_globals(void) {

   struct work_struct *p_worker;

   if (P_CTRL(p_kint_validate)) {

      /* Validate globals... */
      // ...

      /* Prepare for validation which requires 'sleeping' */
      while ( (p_worker = p_ed_alloc_valid()) == NULL); // Should never be NULL
      INIT_WORK(p_worker, p_ed_wq_valid_work);
      /* schedule for execution */
      queue_work(system_unbound_wq, p_worker);

   }
}

void p_ed_validate_current(void) {

   if (!P_CTRL(p_pint_validate))
      return;

   if (p_is_ed_task(current)) {
      p_validate_task_f(current);
   }
}

void p_ed_enforce_validation(void) {

   unsigned long p_flags;

   p_ed_pcfi_cpu(0);

   switch (P_CTRL(p_pint_validate)) {

      case 3:
        p_ed_enforce_validation_paranoid();
        break;

      case 2:
      case 1:
        p_tasks_read_lock(&p_flags);
        if (p_is_ed_task(current)) {
           p_validate_task_f(current);
        }
        p_tasks_read_unlock(&p_flags);
        break;

      case 0:
        break;
   }

   /* Validate critical globals */
//   p_ed_validate_globals();
}

unsigned int p_ed_enforce_validation_paranoid(void) {

   unsigned int p_ret = 0;

   p_ed_pcfi_cpu(0);

   if (!P_CTRL(p_pint_validate))
      goto p_ed_enforce_validation_paranoid_globals;

   /* Validate processes and threads */
   p_ret = p_iterate_lkrg_tasks_paranoid();

p_ed_enforce_validation_paranoid_globals:

   /* Validate critical globals */
   p_ed_validate_globals();

   return p_ret;
}

static void p_ed_pcfi_cache_zero(void *p_arg) {

   unsigned long *p_page = p_arg;

   memset(p_page, 0, P_PCFI_STACK_BUF);
}

static int p_ed_pcfi_cache_init(void) {

   int p_ret = P_LKRG_SUCCESS;

   if ( (p_ed_pcfi_cache = kmem_cache_create("p_ed_pcfi_cache", P_PCFI_STACK_BUF,
                                            0, SLAB_HWCACHE_ALIGN, p_ed_pcfi_cache_zero)) == NULL) {
      p_print_log(P_LKRG_ERR, "kmem_cache_create() for exploit detection pCFI error! :(\n");
      p_ret = -ENOMEM;
   }

   return p_ret;
}

static void p_ed_pcfi_cache_delete(void) {

   if (p_ed_pcfi_cache) {
      kmem_cache_destroy(p_ed_pcfi_cache);
      p_ed_pcfi_cache = NULL;
   }
}

static inline int p_is_obj_on_stack(struct task_struct *p_task, const void *p_addr) {

   void *p_stack = p_task->stack; //task_stack_page(p_task);

   return (p_addr >= p_stack) && (p_addr < (p_stack + THREAD_SIZE));
}

int p_ed_enforce_pcfi(struct task_struct *p_task, struct p_ed_process *p_orig, struct pt_regs *p_regs) {

   unsigned int i = 0;
//   unsigned long p_flags;
   struct stack_trace p_trace;
   const void *p_fp = (const void *)p_regs_get_fp(p_regs);
#ifdef CONFIG_X86
#if defined(CONFIG_UNWINDER_ORC)
   struct unwind_state p_state;
#else
   struct stack_frame p_frame;
#endif
#elif defined(CONFIG_ARM64)
   struct stackframe p_frame;
#elif defined(CONFIG_ARM)
   struct stackframe p_frame;
   const void *p_sp = (const void *)thread_saved_sp(p_task);
   unsigned long p_high, p_low;
#endif
   unsigned int p_offset = 1;
   char p_sym1[KSYM_SYMBOL_LEN];
   char p_not_valid = 0;

   if (p_ed_pcfi_validate_sp(p_task,p_orig,p_regs_get_sp(p_regs))) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Stack pointer corruption (ROP?) - pCFI violation: process[%s | %d] !!!\n",
             p_task->comm,task_pid_nr(p_task));
      // kill this process!
      p_pcfi_kill_task_by_task(p_task);
      p_not_valid = 1;
      goto p_ed_enforce_pcfi_out;
   }

   if (!(P_CTRL(p_pcfi_validate) & 0x2)) {
      goto p_ed_enforce_pcfi_out;
   }

   if (p_orig) {
      if (p_task != p_orig->p_ed_task.p_task) {
         p_print_log(P_LKRG_WARN,
                "<Exploit Detection> [pCFI] Potential kretprobe glitch detected for process[%s] vs orig[%s]\n",
                p_task->comm,
                p_orig->p_ed_task.p_comm);
         p_print_log(P_LKRG_INFO,
                "process[0x%lx | %d | %s] vs orig[0x%lx | %d | %s]\n",
                (unsigned long)p_task,
                task_pid_nr(p_task),
                p_task->comm,
                (unsigned long)p_orig->p_ed_task.p_task,
                p_orig->p_ed_task.p_pid,
                p_orig->p_ed_task.p_comm);
         goto p_ed_enforce_pcfi_out;
      }
   }

   if (!p_is_obj_on_stack(p_task, p_fp)) {
      p_debug_log(P_LKRG_WARN,
                  "Frame pointer is NOT on the stack - CFI is not enforced :(\n");
      goto p_ed_enforce_pcfi_out;
   }

   while ( (p_trace.entries = p_ed_pcfi_alloc()) == NULL); // Should never be NULL

   /* Current logic always call this function with IRQ disabled */
//   local_irq_save(p_flags);

   p_trace.max_entries = P_PCFI_STACK_BUF/sizeof(p_trace.entries[0]);
   p_trace.nr_entries = 0;


#if defined(CONFIG_X86)

#if defined(CONFIG_UNWINDER_ORC)

   if (p_trace.nr_entries < p_trace.max_entries)
      p_trace.entries[p_trace.nr_entries++] = p_regs_get_ip(p_regs);

   do {
      unsigned long p_addr;

      for (unwind_start(&p_state, p_task, p_regs, NULL);
           !unwind_done(&p_state); unwind_next_frame(&p_state)) {
         p_addr = unwind_get_return_address(&p_state);
         if (!p_addr)
            break;
         p_trace.entries[p_trace.nr_entries++] = p_addr;
      }
   } while(0);

#else

   if (p_trace.nr_entries < p_trace.max_entries)
      p_trace.entries[p_trace.nr_entries++] = p_regs_get_ip(p_regs);

   while (p_trace.nr_entries < p_trace.max_entries) {
      p_frame.next_frame = NULL;
      p_frame.return_address = 0;

      if ((unsigned long)p_fp < p_regs_get_sp(p_regs) || !p_is_obj_on_stack(p_task, p_fp))
         break;

      memcpy(&p_frame, p_fp, sizeof(struct stack_frame));
      if (p_frame.return_address) {
         p_trace.entries[p_trace.nr_entries++] = p_frame.return_address;
      }

      if (p_fp == p_frame.next_frame)
         break;

      p_fp = p_frame.next_frame;
    }

#endif

#elif defined(CONFIG_ARM)

   p_frame.fp = (unsigned long)p_fp;
   p_frame.sp = (unsigned long)p_sp;
   p_frame.pc = instruction_pointer(p_regs);

   if (current != p_task) {
      if (p_trace.nr_entries < p_trace.max_entries) {
         if (p_frame.pc) {
            p_trace.entries[p_trace.nr_entries++] = p_frame.pc;
         }
      }
   }

   while (p_trace.nr_entries < p_trace.max_entries) {
      p_fp = (void *)p_frame.fp;
      p_low = p_frame.sp;
      p_high = ALIGN(p_low, THREAD_SIZE);

/*
      if ((unsigned long)p_fp < p_regs_get_sp(p_regs) ||
          !p_is_obj_on_stack(current, p_fp) ||
          (unsigned long)(p_fp) & 0xf)
         break;
*/

      if ((unsigned long)p_fp < p_low + 12 || (unsigned long)p_fp > p_high - 4)
         break;

      p_frame.fp = *(unsigned long *)(p_fp - 12);
      p_frame.sp = *(unsigned long *)(p_fp - 8);
      p_frame.pc = *(unsigned long *)(p_fp - 4);

      if (p_frame.pc) {
         p_trace.entries[p_trace.nr_entries++] = p_frame.pc;
      }

      if (!p_frame.fp && !p_frame.pc)
         break;
   }

#elif defined(CONFIG_ARM64)

   p_frame.fp = (unsigned long)p_fp;
   p_frame.pc = p_regs_get_ip(p_regs);

   if (p_trace.nr_entries < p_trace.max_entries) {
      if (p_frame.pc) {
         p_trace.entries[p_trace.nr_entries++] = p_frame.pc;
      }
   }

   while (p_trace.nr_entries < p_trace.max_entries) {
      p_fp = (void *)p_frame.fp;

      if ((unsigned long)p_fp < p_regs_get_sp(p_regs) ||
          !p_is_obj_on_stack(current, p_fp) ||
          (unsigned long)(p_fp) & 0xf)
         break;

      p_frame.fp = *(unsigned long *)(p_fp);
      p_frame.pc = *(unsigned long *)(p_fp + 8);

      if (p_frame.pc) {
         p_trace.entries[p_trace.nr_entries++] = p_frame.pc;
      }

      if (!p_frame.fp && !p_frame.pc)
         break;
   }

#else

   goto p_ed_enforce_pcfi_unlock_out;

#endif

   if (p_trace.nr_entries) {

   //p_not_valid = 0;
      for (i = 0; i < p_trace.nr_entries-p_offset; i++) {
         if (!P_SYM(p_is_kernel_text_address)(p_trace.entries[i])) {
            if (p_trace.nr_entries-p_offset > 4 && i > 4) {
               memset(p_sym1,0,KSYM_SYMBOL_LEN);
               sprint_symbol_no_offset(p_sym1,p_trace.entries[i-1]);
               if (!strncmp(p_sym1,"ret_from_fork",0xd)) {
                  memset(p_sym1,0,KSYM_SYMBOL_LEN);
                  sprint_symbol_no_offset(p_sym1,p_trace.entries[i-2]);
                  if (!strncmp(p_sym1,"kthread",0x7)) {
                     continue;
                  } else {
                     p_not_valid = 1;
                     break;
                  }
               }
               p_not_valid = 1;
               break;
            } else {
               p_not_valid = 1;
               break;
            }
         }
      }
   }

   if (p_not_valid) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Not valid call - pCFI violation: process[%s | %d] !!!\n",
             p_task->comm,task_pid_nr(p_task));
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> Frame[%d] nr_entries[%d]: [0x%lx]. Full Stack %s\n",
             i,p_trace.nr_entries,p_trace.entries[i],
#if !defined(CONFIG_STACKTRACE)
             "not available (CONFIG_STACKTRACE not enabled)");
#else
             "below:");
      printk(KERN_CRIT "--- . ---\n");
#if defined(CONFIG_ARCH_STACKWALK) || LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0)
      stack_trace_print(p_trace.entries, p_trace.nr_entries, 0);
#else
      print_stack_trace(&p_trace, 0);
#endif
      printk(KERN_CRIT "--- END ---\n");
#endif
      p_pcfi_kill_task_by_task(p_task);
   }

#if !defined(CONFIG_X86) && !defined(CONFIG_ARM64) && !defined(CONFIG_ARM)

p_ed_enforce_pcfi_unlock_out:

#endif

   /* Current logic always call this function with IRQ disabled */
//   local_irq_restore(p_flags);

   p_ed_pcfi_free(p_trace.entries);


p_ed_enforce_pcfi_out:

   return p_not_valid;
}

int p_ed_pcfi_validate_sp(struct task_struct *p_task, struct p_ed_process *p_orig, unsigned long p_sp) {

   unsigned long p_stack = (p_orig) ? (unsigned long) p_orig->p_ed_task.p_stack : 0x0;
   register unsigned long p_stack_offset;
   int p_not_valid = 0;

   if (!P_CTRL(p_pcfi_validate)) {
      return P_LKRG_SUCCESS;
   }

   /*
    * Validate alignment - this test should be passed even if we have a glitching problem
    */
   if (unlikely((p_sp & ~(THREAD_SIZE - 1)) < (THREAD_SIZE / 16))) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process [%s | %d] has invalidly aligned stack pointer!\n",
             p_task->comm,
             task_pid_nr(p_task));
      p_print_log(P_LKRG_INFO, "[base:0x%lx c:0x%lx]\n",
             p_stack,
             p_sp);
      p_not_valid = 1;
   }

   if (!p_stack) {
      return p_not_valid ? P_LKRG_GENERAL_ERROR : P_LKRG_SUCCESS;
   }

   if (p_task != p_orig->p_ed_task.p_task) {
      p_print_log(P_LKRG_WARN,
             "<Exploit Detection> [pCFI - SP] Potential kretprobe glitch detected for process[%s] vs orig[%s]\n",
             p_task->comm,
             p_orig->p_ed_task.p_comm);
      p_print_log(P_LKRG_INFO,
             "process[0x%lx | %d | %s] vs orig[0x%lx | %d | %s]\n",
             (unsigned long)p_task,
             task_pid_nr(p_task),
             p_task->comm,
             (unsigned long)p_orig->p_ed_task.p_task,
             p_orig->p_ed_task.p_pid,
             p_orig->p_ed_task.p_comm);
      p_not_valid = 1;
   }

   /*
    * Validate stack base
    */
   if (unlikely((p_sp & ~(THREAD_SIZE - 1)) != (p_stack & ~(THREAD_SIZE - 1)))) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process [%s | %d] has invalid base for stack pointer!\n",
             p_task->comm,
             task_pid_nr(p_task));
      p_print_log(P_LKRG_INFO, "[base:0x%lx c:0x%lx]\n",
             p_stack,
             p_sp);
      p_not_valid = 1;
   }

   /*
    * Validate if stack is coming from the valid range (CONFIG_VMAP_STACK)
    */

   // TODO

   /*
    * Validate current size of the stack.
    */

   p_stack_offset = p_sp - p_stack;
   if (unlikely(p_stack_offset >= THREAD_SIZE)) {
      p_print_log(P_LKRG_CRIT,
             "<Exploit Detection> process [%s | %d] has invalid stack pointer (stack size mismatch)!\n",
             p_task->comm,
             task_pid_nr(p_task));
      p_print_log(P_LKRG_INFO, "[base:0x%lx c:0x%lx diff:0x%lx]\n",
             p_stack,
             p_sp,
             p_stack_offset);
      p_not_valid = 1;
   }

   return p_not_valid ? P_LKRG_GENERAL_ERROR : P_LKRG_SUCCESS;
}


int p_exploit_detection_init(void) {

   int p_ret;
   const struct p_functions_hooks *p_fh_it;

   p_global_off_cookie = (unsigned long)get_random_long();
   p_global_cnt_cookie = (unsigned long)get_random_long();

   p_global_off_cookie |= P_NORMALIZE_LONG;
   p_global_cnt_cookie |= P_NORMALIZE_LONG;
   p_global_cnt_cookie &= P_MASK_COUNTER;

   if (p_ed_pcfi_cache_init()) {
      p_print_log(P_LKRG_CRIT,
             "Can't initialize ED CFI cache :(\n");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }

   if (p_ed_wq_valid_cache_init()) {
      p_print_log(P_LKRG_CRIT,
             "Can't initialize ED WQ cache :(\n");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }

   P_SYM(p_is_kernel_text_address) = (int (*)(unsigned long))P_SYM(p_kallsyms_lookup_name)("__kernel_text_address");

   if (!P_SYM(p_is_kernel_text_address)) {
      p_print_log(P_LKRG_ERR,
             "[ED] ERROR: Can't find '__kernel_text_address' function :( Exiting...\n");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }

   P_SYM(p_mm_find_pmd) = (pmd_t *(*)(struct mm_struct *, unsigned long))P_SYM(p_kallsyms_lookup_name)("mm_find_pmd");

   if (!P_SYM(p_mm_find_pmd)) {
      p_print_log(P_LKRG_ERR,
             "[ED] ERROR: Can't find 'mm_find_pmd' function :( Exiting...\n");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }

   P_SYM(p_get_seccomp_filter) = (void (*)(struct task_struct *))P_SYM(p_kallsyms_lookup_name)("get_seccomp_filter");

   if (!P_SYM(p_get_seccomp_filter)) {
      p_print_log(P_LKRG_ERR,
             "[ED] ERROR: Can't find 'get_seccomp_filter' function :( Exiting...\n");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
   P_SYM(p_put_seccomp_filter) = (void (*)(struct seccomp_filter *))P_SYM(p_kallsyms_lookup_name)("__put_seccomp_filter");
#else
   P_SYM(p_put_seccomp_filter) = (void (*)(struct task_struct *))P_SYM(p_kallsyms_lookup_name)("put_seccomp_filter");
#endif

   if (!P_SYM(p_put_seccomp_filter)) {
      p_print_log(P_LKRG_ERR,
             "[ED] ERROR: Can't find 'put_seccomp_filter' function :( Exiting...\n");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }

#ifdef CONFIG_SECURITY_SELINUX
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
   P_SYM(p_selinux_enabled)    = (int *)P_SYM(p_kallsyms_lookup_name)("selinux_enabled");
#endif
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)
   P_SYM(p_selinux_state)      = (struct p_selinux_state *)P_SYM(p_kallsyms_lookup_name)("selinux_state");

   if (!P_SYM(p_selinux_state)) {
      p_print_log(P_LKRG_ERR,
             "[ED] ERROR: Can't find 'selinux_state' variable :( Exiting...\n");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }
 #else
   P_SYM(p_selinux_enforcing)  = (int *)P_SYM(p_kallsyms_lookup_name)("selinux_enforcing");

   if (!P_SYM(p_selinux_enforcing)) {
      p_print_log(P_LKRG_ERR,
             "[ED] ERROR: Can't find 'selinux_enforcing' variable :( Exiting...\n");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }
 #endif
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
   if (!P_SYM(p_selinux_enabled)) {
      p_print_log(P_LKRG_ERR,
             "[ED] ERROR: Can't find 'selinux_enabled' variable :( Exiting...\n");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }
#endif
#endif

   if (p_init_rb_ed_pids()) {
      p_print_log(P_LKRG_ERR,
             "ERROR: Can't initialize ED pids cache and red-black tree :(\n");
      p_ret = P_LKRG_GENERAL_ERROR;
      goto p_exploit_detection_init_out;
   }

   // Dump processes and threads
   p_iterate_processes(p_dump_task_f,0x0);

#ifdef CONFIG_SECURITY_SELINUX
   // SELinux information
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)
   p_ed_guard_globals.p_selinux.p_selinux_state.enforcing = P_SYM(p_selinux_state)->enforcing;
 #else
   p_ed_guard_globals.p_selinux.p_selinux_enforcing = *P_SYM(p_selinux_enforcing);
 #endif
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
   p_ed_guard_globals.p_selinux.p_selinux_enabled = *P_SYM(p_selinux_enabled);
#endif
   p_lkrg_counter_lock_init(&p_ed_guard_globals.p_selinux_lock);
#endif

   p_ret = P_LKRG_SUCCESS;

   for (p_fh_it = p_functions_hooks_array; p_fh_it->name != NULL; p_fh_it++) {
      if (p_fh_it->install(p_fh_it->is_isra_safe)) {
         if (!p_fh_it->p_fatal) {
             p_print_log(P_LKRG_WARN, "%s\n", p_fh_it->p_error_message);
             continue;
         }
         p_print_log(P_LKRG_ERR,
                "ERROR: Can't hook %s :(\n", p_fh_it->name);
         p_ret = P_LKRG_GENERAL_ERROR;
         p_exploit_detection_exit();
         break;
      }
   }

p_exploit_detection_init_out:

   return p_ret;
}


void p_exploit_detection_exit(void) {

   const struct p_functions_hooks *p_fh_it;

#if !defined(P_LKRG_DEBUG_BUILD)
   lockdep_off();
#endif

   for (p_fh_it = p_functions_hooks_array; p_fh_it->name != NULL; p_fh_it++) {
      p_fh_it->uninstall();
   }

   mb();

   /* Delete cache for ED wq validation */
   p_ed_wq_valid_cache_delete();
   /* Delete cache for ED CFI validation */
   p_ed_pcfi_cache_delete();
   /* Before deleting cache i should clean each entry! */
   p_delete_rb_ed_pids();

#if !defined(P_LKRG_DEBUG_BUILD)
   lockdep_on();
#endif

   p_print_log(P_LKRG_INFO, "kmem_cache \"p_ed_pids\" destroyed!\n");
}
